﻿#region

using System;
using System.Collections.Generic;
using System.Data.Objects;
using System.Diagnostics;
using System.Linq;
using System.Threading;
using System.Transactions;

#endregion

namespace Core.DAO
{
    public class DAOScope : IDisposable
    {
        [ThreadStatic] private static DAOScope _currentScope;
        private readonly List<ObjectContext> _objectContextList;

        private int _initCount;
        private bool _isDisposed;
        private TransactionScope _transaction;

        /// <summary>
        ///     Default constructor
        /// </summary>
        public DAOScope()
        {
            if (_currentScope != null && !_currentScope._isDisposed) //if current scope existed increase _initCount
            {
                _currentScope._initCount++;
            }
            else //else init TransactionScope
            {
                _objectContextList = new List<ObjectContext>();
                _isDisposed = false; // FxCop recommend
                _initCount = 0; // FxCop recommend
                _transaction = new TransactionScope();
                Debug.WriteLine("Begin transaction");
                Thread.BeginThreadAffinity();
                _currentScope = this;
            }
        }

        #region IDisposable Members

        /// <summary>
        ///     Disposes the DAOScope.
        /// </summary>
        public void Dispose()
        {
            if (_currentScope == null) return;
            if (!_currentScope._isDisposed && _currentScope._initCount == 0)
            {
                foreach (var oc in _currentScope._objectContextList)
                {
                    //GC.SuppressFinalize(oc);
                    //oc.Dispose(); // FxCop recommend
                    Debug.WriteLine("Dispose ObjectContext " + oc.DefaultContainerName);
                }
                if (_currentScope._transaction != null)
                {
                    _currentScope._transaction.Dispose();
                    Debug.WriteLine("Transaction Dispose");
                    _currentScope._transaction = null;
                }
                _currentScope._isDisposed = true;
                _currentScope = null;
                Thread.EndThreadAffinity();
            }
            else
            {
                _currentScope._initCount--;
            }
        }

        #endregion

        /// <summary>
        ///     Returns a reference to a ObjectContext of a specific type that is - or will be -
        ///     created for the current scope. If no scope currently exists, null is returned.
        /// </summary>
        protected internal static TObjectContext GetCurrentObjectContext<TObjectContext>()
            where TObjectContext : ObjectContext, new()
        {
            if (_currentScope == null)
            {
                //_currentScope = new DAOScope();
                return new TObjectContext();
            }
            var ocOfType = _currentScope._objectContextList
                .OfType<TObjectContext>()
                .FirstOrDefault();
            if (ocOfType == null)
            {
                ocOfType = new TObjectContext();
                _currentScope._objectContextList.Add(ocOfType);
                Debug.WriteLine("Begin ObjectContext " + typeof (TObjectContext));
            }
            return ocOfType;
        }

        /// <summary>
        ///     Indicates that all operations within the scope are completed successfully.
        /// </summary>
        public void Complete()
        {
            if (_currentScope != null && _currentScope._initCount == 0)
            {
                _currentScope._transaction.Complete();
                Debug.WriteLine("Transaction Complete");
            }
        }
    }
}